package listener

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"errors"
	"net"
)

// 对TCP消息进行编解码，解决粘包问题

var (
	errCanceled      = errors.New("operation was canceled")
	errWSAECONNRESET = errors.New("wsarecv: An existing connection was forcibly closed by the remote host.")
	errNetClosing    = errors.New("use of closed network connection")
)

// Encode 将消息编码.tcp
func Encode(msg any) ([]byte, error) {
	data, err := json.Marshal(msg)
	if err != nil {
		return nil, err
	}
	// 读取消息的长度，转换成int32类型（占4个字节）
	length := int32(len(data))
	pkg := new(bytes.Buffer)
	// 写入消息头
	err = binary.Write(pkg, binary.LittleEndian, length)
	if err != nil {
		return nil, err
	}
	// 写入消息实体
	err = binary.Write(pkg, binary.LittleEndian, data)
	if err != nil {
		return nil, err
	}
	return pkg.Bytes(), nil
}

// Decode 解码消息.tcp
func Decode(conn net.Conn) (cr []byte, err error) {
	lengthByte := make([]byte, 4)
	if _, err = conn.Read(lengthByte); err != nil {
		if needReadError(err) {
			return nil, err
		}
	}
	lengthBuff := bytes.NewBuffer(lengthByte)
	var length int32
	err = binary.Read(lengthBuff, binary.LittleEndian, &length)
	if err != nil {
		return
	}
	data := make([]byte, 0)
	for int32(len(data)) != length {
		pian := make([]byte, 1024)
		var n int
		if n, err = conn.Read(pian); err != nil {
			return
		}
		if n <= 0 {
			continue
		}
		data = append(data, pian[:n]...)
	}
	return data, err
}

// needReadError 获取IO 错误类型
func needReadError(err error) bool {
	var oe = &net.OpError{}
	if !errors.As(err, &oe) {
		return true
	}
	if errors.Is(err, errCanceled) {
		return false
	}
	if oe.Err.Error() == errWSAECONNRESET.Error() {
		return true
	}
	if oe.Err.Error() == errNetClosing.Error() {
		return false
	}
	return false
}
